package no.nordicsemi.android.nrfmesh.utils;

import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.lifecycle.MediatorLiveData;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import no.nordicsemi.android.mesh.MeshManagerApi;
import no.nordicsemi.android.mesh.MeshNetwork;
import no.nordicsemi.android.mesh.transport.ProvisionedMeshNode;
import no.nordicsemi.android.nrfmesh.ble.BleMeshManager;
import no.nordicsemi.android.nrfmesh.viewmodels.SharedViewModel;
import no.nordicsemi.android.nrfmesh.utils.Utils;
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
import no.nordicsemi.android.support.v18.scanner.ScanCallback;
import no.nordicsemi.android.support.v18.scanner.ScanFilter;
import no.nordicsemi.android.support.v18.scanner.ScanResult;
import no.nordicsemi.android.support.v18.scanner.ScanSettings;
import android.os.ParcelUuid;

/**
 * NodeOnlineHelper 封装“在线识别、过滤显示、离线提示、选中清理”等逻辑，
 * 以便在不同 Activity 中复用，保持行为一致。
 */
public class NodeOnlineHelper {

    public interface ScanGate { boolean allow(); }
    public interface OnlineUpdatedCallback { void onOnlineUpdated(); }

    private final Context context;
    private final SharedViewModel viewModel;
    private final Handler handler;
    private final ScanGate scanGate;
    private OnlineUpdatedCallback onlineUpdatedCallback;

    private final Set<Integer> onlineConnectedAddresses = new HashSet<>();
    private final Set<Integer> onlineScanAddresses = new HashSet<>();
    private final Set<Integer> onlineAddresses = new HashSet<>();

    private final Set<Integer> lastVisibleAddrs = new HashSet<>();
    private final Map<Integer, String> lastVisibleNameByAddr = new HashMap<>();

    public NodeOnlineHelper(@NonNull Context context,
                            @NonNull SharedViewModel viewModel,
                            @NonNull Handler handler,
                            @NonNull ScanGate scanGate) {
        this.context = context;
        this.viewModel = viewModel;
        this.handler = handler;
        this.scanGate = scanGate;
    }

    public void setOnlineUpdatedCallback(OnlineUpdatedCallback cb) {
        this.onlineUpdatedCallback = cb;
    }

    // —— 扫描脉冲：识别 Node Identity ——
    private final ScanCallback proxyScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(final int callbackType, final ScanResult result) {
            try {
                final byte[] serviceData = Utils.getServiceData(result, BleMeshManager.MESH_PROXY_UUID);
                final MeshManagerApi mesh = viewModel.getMeshManagerApi();
                if (mesh != null && serviceData != null && mesh.isAdvertisedWithNodeIdentity(serviceData)) {
                    final MeshNetwork network = viewModel.getNetworkLiveData().getMeshNetwork();
                    if (network != null) {
                        for (ProvisionedMeshNode node : network.getNodes()) {
                            if (mesh.nodeIdentityMatches(node, serviceData)) {
                                onlineScanAddresses.add(node.getUnicastAddress());
                            }
                        }
                    }
                }
            } catch (Exception ignore) { }
        }
    };

    private final Runnable pulseTicker = new Runnable() {
        @Override public void run() {
            startProxyScanPulse();
            // 每 1 秒刷新一次在线识别
            handler.postDelayed(this, 1000);
        }
    };

    public void startTicker() {
        handler.postDelayed(pulseTicker, 1000);
    }

    public void stopTicker() {
        handler.removeCallbacks(pulseTicker);
        stopProxyScanPulse();
    }

    public void startProxyScanPulse() {
        try {
            final boolean permOk = Utils.isBluetoothScanAndConnectPermissionsGranted(context);
            final boolean locOk = !Utils.isLocationRequired(context) || Utils.isLocationPermissionsGranted(context);
            if (!permOk || !locOk) return;
            if (!scanGate.allow()) return;

            onlineScanAddresses.clear();
            final ScanSettings settings = new ScanSettings.Builder()
                    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                    .setReportDelay(0)
                    .setUseHardwareFilteringIfSupported(false)
                    .build();
            final List<ScanFilter> filters = new ArrayList<>();
            filters.add(new ScanFilter.Builder()
                    .setServiceUuid(new ParcelUuid(BleMeshManager.MESH_PROXY_UUID)).build());
            BluetoothLeScannerCompat.getScanner().startScan(filters, settings, proxyScanCallback);

            handler.postDelayed(this::stopProxyScanPulse, 1200);
        } catch (Exception ignore) { }
    }

    public void stopProxyScanPulse() {
        try {
            BluetoothLeScannerCompat.getScanner().stopScan(proxyScanCallback);
        } catch (Exception ignore) { }
        // 合并在线集合并通知
        onlineAddresses.clear();
        onlineAddresses.addAll(onlineConnectedAddresses);
        onlineAddresses.addAll(onlineScanAddresses);
        if (onlineUpdatedCallback != null) onlineUpdatedCallback.onOnlineUpdated();
    }

    // —— 代理连接观察回调（由 Activity 订阅后转调） ——
    public void onProxyConnectionChanged(final Boolean isConnected) {
        if (isConnected != null && !isConnected) {
            onlineConnectedAddresses.clear();
            onlineAddresses.clear();
            onlineAddresses.addAll(onlineScanAddresses);
            if (onlineUpdatedCallback != null) onlineUpdatedCallback.onOnlineUpdated();
        }
    }

    public void onConnectedProxyAddress(final Integer addr) {
        onlineConnectedAddresses.clear();
        if (addr != null) onlineConnectedAddresses.add(addr);
        onlineAddresses.clear();
        onlineAddresses.addAll(onlineConnectedAddresses);
        onlineAddresses.addAll(onlineScanAddresses);
        if (onlineUpdatedCallback != null) onlineUpdatedCallback.onOnlineUpdated();
    }

    // —— 过滤：在线 + 延迟显示 ——
    public List<ProvisionedMeshNode> filterOnlineWithDelay(List<ProvisionedMeshNode> nodes,
                                                           Map<String, Long> pendingVisibility,
                                                           int delayMs) {
        if (nodes == null) return null;
        long now = SystemClock.uptimeMillis();
        List<ProvisionedMeshNode> out = new ArrayList<>();
        for (ProvisionedMeshNode n : nodes) {
            if (!onlineAddresses.contains(n.getUnicastAddress())) {
                continue;
            }
            String name = n.getNodeName();
            Long start = pendingVisibility != null ? pendingVisibility.get(name) : null;
            if (start != null) {
                long elapsed = now - start;
                if (elapsed < delayMs) continue;
            }
            out.add(n);
        }
        return out;
    }

    // —— 设置过滤列表并弹离线 Toast ——
    public void setFilteredNodesWithOfflineToast(MediatorLiveData<List<ProvisionedMeshNode>> target,
                                                 List<ProvisionedMeshNode> newList) {
        try {
            final Set<Integer> newAddrs = new HashSet<>();
            if (newList != null) {
                for (ProvisionedMeshNode n : newList) {
                    if (n != null) newAddrs.add(n.getUnicastAddress());
                }
            }
            for (Integer addr : new HashSet<>(lastVisibleAddrs)) {
                if (!newAddrs.contains(addr)) {
                    final String name = lastVisibleNameByAddr.getOrDefault(addr, "Node " + addr);
                    Toast.makeText(context, name + " 已离线", Toast.LENGTH_SHORT).show();
                }
            }
            lastVisibleAddrs.clear();
            lastVisibleNameByAddr.clear();
            if (newList != null) {
                for (ProvisionedMeshNode n : newList) {
                    if (n == null) continue;
                    final int addr = n.getUnicastAddress();
                    lastVisibleAddrs.add(addr);
                    final String name = (n.getNodeName() != null && n.getNodeName().length() > 0)
                            ? n.getNodeName() : ("Node " + addr);
                    lastVisibleNameByAddr.put(addr, name);
                }
            }
        } catch (Exception ignore) { }
        target.setValue(newList);
    }

    // —— 选中清理：移除离线节点并清除当前选中 ——
    public void pruneSelectionForOfflineNodes(final List<ProvisionedMeshNode> selected,
                                              final ProvisionedMeshNode currentSelected,
                                              final Runnable clearViewModelSelection,
                                              final Runnable updateAdapterSelection) {
        if (selected != null && !selected.isEmpty()) {
            final List<ProvisionedMeshNode> toRemove = new ArrayList<>();
            for (ProvisionedMeshNode sn : selected) {
                if (sn != null && !onlineAddresses.contains(sn.getUnicastAddress())) {
                    toRemove.add(sn);
                }
            }
            selected.removeAll(toRemove);
        }
        if (currentSelected != null && !onlineAddresses.contains(currentSelected.getUnicastAddress())) {
            if (clearViewModelSelection != null) clearViewModelSelection.run();
        }
        if (updateAdapterSelection != null) updateAdapterSelection.run();
    }

    public boolean isOnline(final int unicastAddress) {
        return onlineAddresses.contains(unicastAddress);
    }
}